Skip to main content

pmatch

When we had our first look at the structs, we hinted at the possibility of custom control flows.

These are possible thanks to the pmatch construct.

To understand why this is extremely useful, let's take our Toy struct we defined looking at pstruct.

const Toy = pstruct({
Stick: {},
Ball: {
size: int,
isSoft: bool
},
Mailman: {
name: str,
age: Age.type
}
})

And let's say we want to have a function that extracts the name of the mailman our dog plays with when we're out. It would look something like this:

const getMailmanName = plam( Toy.type, str )
( toy =>
pmatch( toy )
.onMailman( mailman => mailman.name )
.onStick( _ => pStr("") )
.onBall( _ => pStr("") )
)

pmatch is taking a struct Term and returns a Typescript object with all the names of possible constructors that struct has, based on its definition.

It then executes the branch based on the constructor used to get the struct instance.

A pmatch branch gets as input the instance of the sturct with all fields aviable trough dot notation.

This way the defined function returns the name of the mailman if the Toy was actually constructed using the Mailman constructor; otherwise it returns an empty string.

the underscore (_) wildcard

pmatch will force you to cover the cases for all constructors; but many times we only want to do something if the struct was built using one specific constructor without regard for any other constructors.

In fact we found ourselves in a very similar case in the example above: we want to do something only in the Mailman case but not in the others.

For situations like these there is the underscore (_) wildcard, that allows us to rewrite our function as follows:

const getMailmanName = plam( Toy.type, str )
( toy =>
pmatch( toy )
.onMailman( mailman => mailman.name )
._( _ => pStr("") )
)

This not only makes the code more readable but in the vast majority of the cases it also makes it more efficient!

inline dot notation

Now that we have a way to extract the name of the mailman from a Toy, we need to pass the actual toy to the fuction we just defined.

Using the pmatch function, our code would look like this:

// remember the definition of `Dog`
const Dog = pstruct({
Dog: {
name: str,
age: Age.type,
favouriteToy: Toy.type
}
});

const getMailmanNameFromDog = plam( Dog.type, str )
( dog =>
pmatch( dog )
.onDog( dogInstance =>
getMailmanName.$( dogInstance.favouriteToy )
)
)

This works just fine but is a lot of code just to get a field of a constructor we know is unique...

Fortunately for us, if the struct definition has only one possible constructor, this struct term directly exposes the fields; so that we can threat it as a normal javascript object

This allows us to rewrite the function as

const getMailmanNameFromDog = plam( Dog.type, str )
( dog =>
getMailmanName.$( dog.favouriteToy )
)

which is a lot cleaner!